﻿namespace GSMock
{
    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Xml;
    using System.Xml.XPath;

    /// <summary>
    ///     Transforms a results file with a NameValueCollection of parameters.
    /// </summary>
    public class ResultsTransformer
    {
        /// <summary>
        ///     Initializes a new instance of the <see cref="ResultsTransformer"/> class.
        /// </summary>
        /// <param name="results">The results file to transform.</param>
        /// <param name="parameters">The parameters used to transform the results file with.</param>
        public ResultsTransformer(XmlDocument results, NameValueCollection parameters)
        {
            // Bind class properties.
            this.Results = results;
            this.Parameters = parameters;

            // Initialize ignore parameters.
            this.IgnoreParameters = new List<string>();
            this.IgnoreParameters.Add("site");
        }

        /// <summary>
        ///     Gets or sets the XmlDocument containing the results to be transformed.
        /// </summary>
        private XmlDocument Results
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the parameters to transform the results file with.
        /// </summary>
        private NameValueCollection Parameters
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the parameters which should be ignored in the transformation.
        /// </summary>
        private List<string> IgnoreParameters
        {
            get;
            set;
        }

        /// <summary>
        ///     Transforms the results file.
        /// </summary>
        /// <returns>
        ///     The results file transformed with the parameters.
        /// </returns>
        public XmlDocument Transform()
        {
            foreach (string key in this.Parameters.Keys)
            {
                if (!this.IgnoreParameters.Contains(key, StringComparer.InvariantCultureIgnoreCase))
                { 
                    this.AddOrUpdateParameter(key, this.Parameters[key]);
                }
            }

            return this.Results;
        }

        /// <summary>
        ///   Adds or updates the parameter in the results file.
        /// </summary>
        /// <param name="name">The parameter name.</param>
        /// <param name="value">The parameter value.</param>
        private void AddOrUpdateParameter(string name, string value)
        {
             // Currently, any spaces in original_value are replaced with '+'. This should only happen on custom parameters,
             // However at this stage, we do-not define a list or standard parameters so we apply this logic to all.
             // http://code.google.com/apis/searchappliance/documentation/68/xml_reference.html#results_xml
            string originalValue = value.Replace(' ', '+');
            
            XmlNode node = this.Results.SelectSingleNode(string.Format("GSP/PARAM[@name='{0}']", name));

            if (node == null)
            {
                // Create a new parameter node.
                XmlElement newParam = this.Results.CreateElement("PARAM");
                newParam.SetAttribute("name", name);
                newParam.SetAttribute("value", value);
                newParam.SetAttribute("original_value", originalValue);

                // And insert after the last parameter node.
                XPathNavigator lastParam = this.Results.SelectSingleNode("GSP/PARAM[last()]").CreateNavigator();
                lastParam.InsertAfter(newParam.CreateNavigator());
            }
            else
            {
                // Update the existing node value attribute
                if (node.Attributes["value"] != null)
                {
                    node.Attributes["value"].InnerText = value;
                }
                else
                {
                    node.CreateNavigator().CreateAttribute(null, "value", null, originalValue);
                }

                // Update the existing node original_value attribute
                if (node.Attributes["original_value"] != null)
                {
                    node.Attributes["original_value"].InnerText = originalValue;
                }
                else
                {
                    node.CreateNavigator().CreateAttribute(null, "original_value", null, originalValue);
                }
            }
        }
    }
}
